package com.slipper.util;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import com.alibaba.excel.EasyExcel;
import com.slipper.core.excel.ColumnWidthStrategy;
import com.slipper.core.excel.DateTimeDateConverter;
import com.slipper.core.file.FileServiceUtil;
import com.slipper.exception.CommonException;
import jakarta.servlet.http.HttpServletResponse;

import org.apache.tomcat.util.http.fileupload.IOUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;

public final class FileUtil {
    private static final Logger logger = LoggerFactory.getLogger(FileUtil.class);

    /**
     * 下载文件
     * @param response HttpServletResponse
     * @param fileName 输出的文件名
     * @param inputStream 文件流
     */
    public static void download(HttpServletResponse response, String fileName, InputStream inputStream, long length) {
        if (Tool.isBlank(fileName)) {
            fileName = "default"; // 定义保存的文件名字
        }
        String trueName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + trueName + "\"");
        response.setHeader("Content-Length", "" + length);
        try {
            IOUtils.copy(inputStream, response.getOutputStream());
        } catch (IOException e) {
            logger.error("文件下载出错：{}", e.getMessage(), e);
            throw new CommonException("文件下载出错！", e);
        }
    }

    /**
     * 下载文件
     * @param response HttpServletResponse
     * @param fileName 输出的文件名
     * @param content 文件内容
     */
    public static void downloadContent(HttpServletResponse response, String fileName, String content) {
        download(response, fileName, content.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 下载文件
     * @param response HttpServletResponse
     * @param fileName 输出的文件名
     * @param bytes 文件内容
     */
    public static void download(HttpServletResponse response, String fileName, byte[] bytes) {
        try {
            if (fileName == null || fileName.trim().isEmpty()) {
                fileName = "default"; // 定义保存的文件名字
            }
            String trueName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + trueName + "\"");
            response.setHeader("Content-Length", "" + bytes.length);
            response.getOutputStream().write(bytes);
        } catch (IOException e) {
            logger.error("文件下载出错：{}", e.getMessage(), e);
            throw new CommonException("文件下载出错！", e);
        }
    }

    /**
     * 下载Excel
     */
    public static void downloadExcel(
            HttpServletResponse response,
            String fileName,
            List<List<String>> headList,
            List<Integer> widthList,
            List<List<Object>> dataList) throws IOException {
        response.setHeader("Access-Control-Allow-Headers", "Content-Disposition");
        String trueName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
        response.setHeader("Content-Disposition", "attachment; filename=\"" + trueName + "\"");
        response.setContentType("application/octet-stream");

        EasyExcel.write(response.getOutputStream())
                .head(headList)
                .registerConverter(new DateTimeDateConverter())
                .registerWriteHandler(new ColumnWidthStrategy(widthList))
                .sheet("Sheet0")
                .doWrite(dataList);
    }

    /**
     * 下载模板
     * @param response HttpServletResponse
     */
    public static void downloadTpl(HttpServletResponse response, String tplFileName, String fileName) {
        downloadTpl(response, tplFileName, fileName, "application/octet-stream");
    }

    /**
     * 下载模板
     * @param response HttpServletResponse
     */
    public static void downloadTpl(
            HttpServletResponse response,
            String tplFileName,
            String fileName,
            String contentType) {
        if (Tool.isBlank(fileName)) {
            fileName = "default"; // 定义保存的文件名字
        }
        String trueName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
        response.setContentType(contentType);
        response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_TYPE);
        response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);
        response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + trueName);
        try (InputStream inputStream = new BufferedInputStream(Objects.requireNonNull(FileUtil.class
                .getClassLoader()
                .getResourceAsStream("templates/" + tplFileName)))) {
            IOUtils.copy(inputStream, response.getOutputStream());
        } catch (IOException e) {
            logger.error("文件下载出错：{}", e.getMessage(), e);
            throw new CommonException("文件下载出错！", e);
        }
    }
    
   public static void downloadZip(
           HttpServletResponse response,
           String zipFileName,
           List<String> fileNameList,
           List<String> filePathList) throws Exception {
        response.setContentType("application/octet-stream");
        String trueName = URLEncoder.encode(zipFileName, StandardCharsets.UTF_8);
        response.setHeader("Content-Disposition", "attachment; filename=\"" + trueName + "\"");

        ByteArrayOutputStream output = new ByteArrayOutputStream();
        CheckedOutputStream cos = new CheckedOutputStream(output, new CRC32());
        ZipOutputStream zos = new ZipOutputStream(cos);

        Set<String> fileNameSet = new HashSet<>();
        for (int i = 0; i < filePathList.size(); i++) {
            String file = filePathList.get(i);
            String fileName = fileNameList.get(i);
            
            int suffix = 1;
            while(!fileNameSet.add(fileName)) {
                fileName = fileNameList.get(i) + "_" + suffix;
                suffix++;
            }

            //构建输入流
            BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(FileServiceUtil.readFile(file)));
            //创建文件（zip里面的文件）
            ZipEntry entry = new ZipEntry(fileName);
            //放入文件
            zos.putNextEntry(entry);
            //写入文件
            IOUtils.copy(bis, zos);
            //关闭流
            bis.close();
        }

        zos.closeEntry();
        zos.close();

        //设置返回信息
        response.setHeader("Content-Length", String.valueOf(output.size()));
        IOUtils.copy(new ByteArrayInputStream(output.toByteArray()), response.getOutputStream());

        //创建完压缩文件后关闭流
        cos.close();
        output.close();
   }

    public static byte[] readBytes(String path) {
        return readBytes(new File(path));
    }

    public static byte[] readBytes(File file) {
        try (FileInputStream fis = new FileInputStream(file)) {
            return readBytes(fis);
        } catch (IOException e) {
            logger.error("文件读取出错：{}", e.getMessage(), e);
            throw new CommonException("文件读取失败！", e);
        }
    }

    public static byte[] readBytes(InputStream is) {
        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = is.read(buffer)) != -1) {
                os.write(buffer, 0, length);
            }
            return os.toByteArray();
        } catch (IOException e) {
            logger.error("文件下载出错：{}", e.getMessage(), e);
            throw new CommonException("文件读取失败！", e);
        }
    }

    public static String readToBase64(String path) {
        File file = new File(path);
        if (!file.exists()) return null;

        try (FileInputStream fis = new FileInputStream(file);
                ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = fis.read(buffer)) != -1) {
                os.write(buffer, 0, length);
            }
            return Base64.getEncoder().encodeToString(os.toByteArray());
        } catch (IOException e) {
            logger.error("文件下载出错：{}", e.getMessage(), e);
            throw new CommonException("文件读取失败！", e);
        }
    }

    public static void saveBase64(String path, String base64) {
        File file = new File(path);
        File directory = new File(file.getParent());
        if (!directory.exists() && !directory.mkdirs()) {
            throw new CommonException("文件夹[" + file.getParent() + "]创建失败！");
        }
        try (FileOutputStream fos = new FileOutputStream(file)) {
            fos.write(Base64.getDecoder().decode(base64));
        } catch (IOException e) {
            logger.error("文件下载出错：{}", e.getMessage(), e);
            throw new CommonException("文件保存失败！", e);
        }
    }
}
